package org.anachronos.jeep.agent.instrumentation;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.ow2.util.asm.ClassAdapter;
import org.ow2.util.asm.ClassVisitor;
import org.ow2.util.asm.MethodVisitor;
import org.ow2.util.asm.commons.AdviceAdapter;


class TransactionClassAdapter extends ClassAdapter {
	private static final String TRANSACTION_MANAGER_CLAZZ = 
		TransactionTracingConstants.TRANSACTION_MANAGER_CLAZZ.replace(".", "/");
	private static final String USER_TRANSACTION_CLAZZ = 
		TransactionTracingConstants.USER_TRANSACTION_CLAZZ.replace(".", "/");
	private static final String TRANSACTION_CLAZZ = 
		TransactionTracingConstants.TRANSACTION_CLAZZ.replace(".", "/");
	
	private final static Set<String> END_METHODS = 
		new HashSet<String>(Arrays.asList(TransactionTracingConstants.END_METHODS));
	
	enum TransactionClassType {
		TRANSACTION, USER_TRANSACTION, TRANSACTION_MANAGER
	}
	
	private TransactionClassType type;
	private String className;
	
	public TransactionClassAdapter(final ClassVisitor cv) {
		super(cv);
	}

	@Override
	public void visit(int version, int access, String name, String signature,
			String superName, String[] interfaces) {
		super.visit(version, access, name, signature, superName, interfaces);
		
		this.className = name;
		
		for (final String interfaceName : interfaces) {
			if (TRANSACTION_CLAZZ.equals(interfaceName)) {
				type = TransactionClassType.TRANSACTION;
			}
			if (USER_TRANSACTION_CLAZZ.equals(interfaceName)) {
				type = TransactionClassType.USER_TRANSACTION;
				break;
			}
			if (TRANSACTION_MANAGER_CLAZZ.equals(interfaceName)) {
				type = TransactionClassType.TRANSACTION_MANAGER;
				break;
			}
		}
	}
	
	@Override
	public MethodVisitor visitMethod(final int access, final String name, final String description,
			final String signature, final String[] exceptions) {
		final MethodVisitor mv = cv.visitMethod(access, name, description, signature, exceptions);
		if (type != null) {
			switch (type) {
			case USER_TRANSACTION:
			case TRANSACTION_MANAGER:
				if (name.equals(TransactionTracingConstants.BEGIN_METHOD)) {
					final AdviceAdapter adviceAdapter =
						new TransactionBeginAdviceAdapter(mv, access, name, description);
					return adviceAdapter;
				} 
			case TRANSACTION:
				if (END_METHODS.contains(name)) {
					final AdviceAdapter adviceAdapter = 
						new TransactionEndAdviceAdapter(mv, access, name, description, className);
					return adviceAdapter;					
				}
			}
		}
		return mv;
	}
}
